#include <QApplication>
#include <QDir>
#include <QDataStream>
#include <QString>
#include <QDebug>
#include <QBuffer>
#include <typeinfo>
#include "mini2440Capturethread.h"
#include <iostream>
#include <QFile>

CaptureThread::CaptureThread(QWidget *parent) :
    QThread(parent)
{
    this->parent=(videowidget*)parent;
    devam=false;
    fd = -1;
}

void CaptureThread::run(){
//do real stuff

fd = -1;
dev_name = "/dev/video0";

    fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
    if (fd < 0) {
           qDebug("Cannot open device");
           //exit(EXIT_FAILURE);
           return;
    }

    //static struct v4lconvert_data *v4lconvert_data;
    static struct v4l2_format src_fmt;

    CLEAR(fmt);
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width       = 640;
    fmt.fmt.pix.height      = 480;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    xioctl(fd, VIDIOC_S_FMT, &fmt);
    if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
    {
           printf("Libv4l didn't accept RGB24 format. Can't proceed.\n");
           //exit(EXIT_FAILURE);
           return;
    }

    if ((fmt.fmt.pix.width != 320) || (fmt.fmt.pix.height != 240))
           printf("Warning: driver is sending image at %dx%d\n",
                   fmt.fmt.pix.width, fmt.fmt.pix.height);


    xioctl(fd, VIDIOC_S_FMT, &src_fmt);

    CLEAR(req);
    req.count = 2;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    xioctl(fd, VIDIOC_REQBUFS, &req);

    buffers = (buffer*)calloc(req.count, sizeof(*buffers));
    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
           CLEAR(buf);

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
           buf.memory      = V4L2_MEMORY_MMAP;
           buf.index       = n_buffers;

           xioctl(fd, VIDIOC_QUERYBUF, &buf);

           buffers[n_buffers].length = buf.length;
           buffers[n_buffers].start = mmap(NULL, buf.length,
                         PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd, buf.m.offset);

           if (MAP_FAILED == buffers[n_buffers].start) {
                   qDebug("mmap");
                   //exit(EXIT_FAILURE);
                   return;
           }
    }

    for (int i = 0; i < n_buffers; ++i) {
           CLEAR(buf);
           buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
           buf.memory = V4L2_MEMORY_MMAP;
           buf.index = i;
           xioctl(fd, VIDIOC_QBUF, &buf);
    }
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    xioctl(fd, VIDIOC_STREAMON, &type);

    while(devam){
        do {
                FD_ZERO(&fds);
                FD_SET(fd, &fds);

                /* Timeout. */
                tv.tv_sec = 2;
                tv.tv_usec = 0;

                r = select(fd + 1, &fds, NULL, NULL, &tv);
        } while ((r == -1 && (errno = EINTR)));
        if (r == -1) {
                qDebug("select");
                //exit(1) ;
                return;
        }

        CLEAR(buf);
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        xioctl(fd, VIDIOC_DQBUF, &buf);

        try
        {
            frame_save(buffers[buf.index].start,fmt.fmt.pix.width,fmt.fmt.pix.height);
        }catch(...){}
        xioctl(fd, VIDIOC_QBUF, &buf);
   }
    try{
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    xioctl(fd, VIDIOC_STREAMOFF, &type);
    for (int i = 0; i < n_buffers; ++i)
           munmap(buffers[i].start, buffers[i].length);

        close(fd);
    }catch(...){}
}

int CaptureThread::clamp (double x)
{
        int r = x;      /* round to nearest */

        if (r < 0)         return 0;
        else if (r > 255)  return 255;
        else               return r;
}

int CaptureThread::frame_save(const void *p,int width, int height)
{
    static int packed_value, i,k;
    char header[50];//="P6\n640 480 255\n";
    sprintf(header,"P6\n%d %d 255\n", width/4*3,height/4*3);
    static unsigned char *dest_buf;
    dest_buf = (unsigned char*) malloc(fmt.fmt.pix.sizeimage*3/2);
    memset(dest_buf,0,fmt.fmt.pix.sizeimage*3/2);

    i = 0;
    k = 0;
    
    int Y0, Y1, Cb, Cr;            /* gamma pre-corrected input [0;255] */
    int ER0,ER1,EG0,EG1,EB0,EB1;    /* output [0;255] */
    double r0,r1,g0,g1,b0,b1;             /* temporaries */
    double y0,y1, pb, pr;

    while(i < (width * height/2))
    {

        packed_value = *((int*)p+i);

        Y0 = (char)(packed_value & 0xFF);
        Cb = (char)((packed_value >> 8) & 0xFF);
        Y1 = (char)((packed_value >> 16) & 0xFF);
        Cr = (char)((packed_value >> 24) & 0xFF);

        // Strip sign values after shift (i.e. unsigned shift)
        Y0 = Y0 & 0xFF;
        Cb = Cb & 0xFF;
        Y1 = Y1 & 0xFF;
        Cr = Cr & 0xFF;

        y0 = 1.1643 * (Y0 - 0.0625);
        y1 = 1.1643 * (Y1 - 0.0625);
        pb = (255 / 224.0) * (Cb - 128);
        pr = (255 / 224.0) * (Cr - 128);

        // Generate first pixel
        r0 = 1.0 * y0 + 0       * pb + 1.5958   * pr;
        g0 = 1.0 * y0 - 0.39173 * pb - 0.8129   * pr;
        b0 = 1.0 * y0 + 2.017   * pb + 0        * pr;

        // Generate next pixel - must reuse pb & pr as 4:2:2
        r1 = 1.0 * y1 + 0       * pb + 1.5958    * pr;
        g1 = 1.0 * y1 - 0.39173 * pb - 0.8129    * pr;
        b1 = 1.0 * y1 + 2.017   * pb + 0        * pr;

        ER0 = clamp (r0);
        ER1 = clamp (r1);
        EG0 = clamp (g0);
        EG1 = clamp (g1);
        EB0 = clamp (b0);
        EB1 = clamp (b1);

        if (i % 4 == 3)
        {
            i++;
            continue;
        }
        if ((i / width) % 4 == 3)
        {
            i++;
            continue;
        }
        dest_buf[k*6]     = ER0;
        dest_buf[k*6 + 1] = EG0;
        dest_buf[k*6 + 2] = EB0;
        dest_buf[k*6 + 3] = ER1;
        dest_buf[k*6 + 4] = EG1;
        dest_buf[k*6 + 5] = EB1;
        i++;
        k++;
    }

    unsigned char* asil=(unsigned char*)malloc(fmt.fmt.pix.sizeimage*3/2+qstrlen(header));
    memmove(asil, dest_buf, fmt.fmt.pix.sizeimage);
    memmove(asil+qstrlen(header), asil, fmt.fmt.pix.sizeimage*3/2);
    memcpy(asil,header,qstrlen(header));

    QImage qq;

    if(qq.loadFromData(asil,fmt.fmt.pix.sizeimage*3/2+qstrlen(header),"PPM")){
        if(parent->isVisible()){
            QImage q1(qq);
            parent->img=q1;
            parent->update();
        }
    }
    if(asil)
        free(asil);
    if(dest_buf)
        free(dest_buf);

    return 0;
}

CaptureThread::~CaptureThread()
{
    try{
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    xioctl(fd, VIDIOC_STREAMOFF, &type);
    /*for (int i = 0; i < n_buffers; ++i)
           v4l2_munmap(buffers[i].start, buffers[i].length);*/

        close(fd);
    }catch(...){}
    fd = -1;
}
void CaptureThread::stopUlan()
{
    devam=false;
    try{
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    xioctl(fd, VIDIOC_STREAMOFF, &type);
    /*for (int i = 0; i < n_buffers; ++i)
           v4l2_munmap(buffers[i].start, buffers[i].length);*/

        close(fd);
    }catch(...){}
    fd = -1;

}
void CaptureThread::startUlan()
{
    this->start();

}
/*
void CaptureThread::onStopCapture()
{
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    xioctl(fd, VIDIOC_STREAMOFF, &type);
    for (int i = 0; i < n_buffers; ++i)
           v4l2_munmap(buffers[i].start, buffers[i].length);
    v4l2_close(fd);
}*/



